//
// Created by Administrator on 2025/1/25.
//

#include <fstream>
#include "Pool.h"

Pool::Pool() {
    //加载配置文件
    if(!loadConfigFile("/home/china/Codes/chat/MySQLConfig.json")){
        LOG_WARN<<"配置文件加载失败";
        return ;
    }
    //创建初始数量的连接
    for(int i = 0 ; i < _poolConfig.initSize ; ++i){
        Connection* p = new Connection();
        if(p->connect(_poolConfig.mysqlConfig)){
            _connQue.push(p);
            p->refreshAliveTime();
        }
    }
    _connCnt = _connQue.size();//记录上面成功创建了几个线程
    LOG_INFO<<"创建的连接数量:"<<_connCnt;

    //启动一个新线程，作为连接的生产者
    std::thread produce(std::bind(&Pool::produceConnTask,this));
    //启动一个新线程，用于扫描 空闲时间 > maxIdleTime 的连接，然后回收多余的连接
    std::thread scan(std::bind(&Pool::scanConnTask,this));
    produce.detach();
    scan.detach();
}

//扫描连接线程函数--处理多余的空闲连接
void Pool::scanConnTask() {
//    LOGI<<"扫描线程启动";
    for(;;){
        //通过睡眠 模拟定时效果
        std::this_thread::sleep_for(std::chrono::seconds(_poolConfig.maxIdleTime));

        //扫描整个队列，释放多余连接
        std::unique_lock lock(_queueMutex);
        while(_connCnt > _poolConfig.maxSize){
            Connection *p = _connQue.front();//若队头没有超时，则后面还不会超时
            if(p->getAliveTime() > (_poolConfig.maxIdleTime*1000)){
                _connQue.pop();
                delete p;
                _connCnt = _connQue.size();
                LOG_INFO<<"删除一个多余的空闲连接，当前连接量:"<<_connCnt;
            }else{
                break;
            }
        }
    }
}

//生产者线程函数
void Pool::produceConnTask() {
    LOG_INFO<<"生产者线程启动";
    for(;;){
        std::unique_lock lock(_queueMutex);
        while (!_connQue.empty()){//如果队列不是空的，说明还有连接，此时就不必继续生产
            cv.wait(lock);
        }

        //确保没有超过设置的最大连接数
        if(_connCnt < _poolConfig.maxSize){
            Connection* p = new Connection();
            if(p->connect(_poolConfig.mysqlConfig)){
                //连接成功
                p->refreshAliveTime();
                _connQue.push(p);
                _connCnt = _connQue.size();
                LOG_INFO<<"成功生产一个连接，当前连接量:"<<_connCnt;
            }
        }
        //通知消费者线程，可以进行消费
        cv.notify_all();
    }
}

//从连接池获取一个空闲的连接
std::shared_ptr<Connection> Pool::getFreeConn() {
    std::unique_lock lock(_queueMutex);
    while(_connQue.empty()){
        if(std::cv_status::timeout == cv.wait_for(lock,std::chrono::milliseconds(_poolConfig.connectTimeout))){
            if(_connQue.empty()){
                LOG_INFO<<"获取空闲连接超时！";
                return nullptr;
            }
        }
    }
    //参数二为自定义的删除器，这里再次将其放入队列，不让其析构
    std::shared_ptr<Connection> sp(_connQue.front(), [&](Connection* p){
        //考虑线程安全
        std::unique_lock lock(_queueMutex);
        p->refreshAliveTime();
        _connQue.push(p);
    });
    _connQue.pop();
    cv.notify_all();//通知生产者进行生产，生产者会检查是不是已经为空
    cv.notify_all();
    return sp;
}

//从配置文件加载配置
bool Pool::loadConfigFile(const std::string& file) {
    LOG_INFO<<file;
    if(file.empty()){
        LOG_WARN<<"无效文件名";
        return false;
    }
    std::ifstream f(file, std::ios::in | std::ios::binary);
    if (!f.is_open()) {
        LOG_WARN << "无法打开文件: " << file;
        return false;
    }
    // 读取文件内容
    std::ostringstream oss;
    oss << f.rdbuf();
    std::string file_str = oss.str();
    // 关闭文件
    f.close();

    size_t npos1 = file_str.find("mysqlConfig");
    if(npos1 == std::string::npos){
        LOG_ERROR<<"没有找到 mysqlConfig 字段";
        return false;
    }

    nlohmann::json js = nlohmann::json::parse(file_str);
    _poolConfig.mysqlConfig.ip = js["mysqlConfig"]["ip"];
    _poolConfig.mysqlConfig.port = js["mysqlConfig"]["port"];
    _poolConfig.mysqlConfig.dbName = js["mysqlConfig"]["dbName"];
    _poolConfig.mysqlConfig.user = js["mysqlConfig"]["user"];
    _poolConfig.mysqlConfig.pwd = js["mysqlConfig"]["pwd"];
    _poolConfig.maxIdleTime = js["maxIdleTime"];
    _poolConfig.maxSize = js["maxSize"];
    _poolConfig.connectTimeout = js["connectTimeout"];
    _poolConfig.initSize = js["initSize"];

    return true;
}

Pool *Pool::getInstance() {
    static Pool pool;
    return &pool;
}

